/*
 * Copyright (C) 2016-2020 52°North Initiative for Geospatial Open Source
 * Software GmbH
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 *
 * If the program is linked with libraries which are licensed under one of
 * the following licenses, the combination of the program with the linked
 * library is not considered a "derivative work" of the program:
 *
 *       • Apache License, version 2.0
 *       • Apache Software License, version 1.0
 *       • GNU Lesser General Public License, version 3
 *       • Mozilla Public License, versions 1.0, 1.1 and 2.0
 *       • Common Development and Distribution License (CDDL), version 1.0
 *
 * Therefore the distribution of the program linked with libraries licensed
 * under the aforementioned licenses, is permitted by the copyright holders
 * if the distribution is compliant with both the GNU General Public
 * License version 2 and the aforementioned licenses.
 *
 * As an exception to the terms of the GPL, you may copy, modify,
 * propagate, and distribute a work formed by combining 52°North WPS
 * GeoTools Modules with the Eclipse Libraries, or a work derivative of
 * such a combination, even if such copying, modification, propagation, or
 * distribution would otherwise violate the terms of the GPL. Nothing in
 * this exception exempts you from complying with the GPL in all respects
 * for all of the code used other than the Eclipse Libraries. You may
 * include this exception and its grant of permissions when you distribute
 * 52°North WPS GeoTools Modules. Inclusion of this notice with such a
 * distribution constitutes a grant of such permissions. If you do not wish
 * to grant these permissions, remove this paragraph from your
 * distribution. "52°North WPS GeoTools Modules" means the 52°North WPS
 * modules using GeoTools functionality - software licensed under version 2
 * or any later version of the GPL, or a work based on such software and
 * licensed under the GPL. "Eclipse Libraries" means Eclipse Modeling
 * Framework Project and XML Schema Definition software distributed by the
 * Eclipse Foundation and licensed under the Eclipse Public License Version
 * 1.0 ("EPL"), or a work based on such software and licensed under the EPL.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 */
package org.n52.javaps.gt.io.datahandler.generator;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.inject.Inject;

import org.geotools.data.DataStoreFactorySpi;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureStore;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.feature.FeatureCollection;
import org.geotools.referencing.CRS;
import org.n52.javaps.gt.io.GTHelper;
import org.n52.javaps.gt.io.data.binding.complex.GTVectorDataBinding;
import org.n52.javaps.io.Data;
import org.opengis.feature.IllegalAttributeException;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.FactoryException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GTBinDirectorySHPGenerator {

    private static Logger LOGGER = LoggerFactory.getLogger(GTBinDirectorySHPGenerator.class);

    private static final String SHP = ".shp";

    @Inject
    private GTHelper gtHelper;

    public File writeFeatureCollectionToDirectory(Data<?> data) throws IOException {
        return writeFeatureCollectionToDirectory(data, null);
    }

    public File writeFeatureCollectionToDirectory(Data<?> data,
            File parent) throws IOException {
        GTVectorDataBinding binding = (GTVectorDataBinding) data;
        FeatureCollection<?, ?> originalCollection = binding.getPayload();

        SimpleFeatureCollection collection = gtHelper.createCorrectFeatureCollection(originalCollection);

        return createShapefileDirectory(collection, parent);
    }

    /**
     * Transforms the given {@link FeatureCollection} into a zipped SHP file
     * (.shp, .shx, .dbf, .prj) and returs its Base64 encoding
     *
     * @param collection
     *            the collection to transform
     * @return the zipped shapefile
     * @throws IOException
     *             If an error occurs while creating the SHP file or encoding
     *             the shapefile
     * @throws IllegalAttributeException
     *             If an error occurs while writing the features into the the
     *             shapefile
     */
    @SuppressWarnings("unchecked")
    private File createShapefileDirectory(FeatureCollection<SimpleFeatureType, SimpleFeature> collection,
            File parent) throws IOException, IllegalAttributeException {

        File parentCopy = parent;

        if (parentCopy == null) {
            File tempBaseFile = File.createTempFile("resolveDir", ".tmp");
            tempBaseFile.deleteOnExit();
            parentCopy = tempBaseFile.getParentFile();
        }

        if (parentCopy == null || !parentCopy.isDirectory()) {
            throw new IllegalStateException("Could not find temporary file directory.");
        }

        File shpBaseDirectory = new File(parentCopy, UUID.randomUUID().toString());

        if (!shpBaseDirectory.mkdir()) {
            throw new IllegalStateException("Could not create temporary shp directory.");
        }

        File tempSHPfile = File.createTempFile("shp", SHP, shpBaseDirectory);
        tempSHPfile.deleteOnExit();
        DataStoreFactorySpi dataStoreFactory = new ShapefileDataStoreFactory();
        Map<String, Serializable> params = new HashMap<String, Serializable>();
        params.put("url", tempSHPfile.toURI().toURL());
        params.put("create spatial index", Boolean.TRUE);

        ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);

        newDataStore.createSchema(collection.getSchema());
        if (collection.getSchema().getCoordinateReferenceSystem() == null) {
            try {
                newDataStore.forceSchemaCRS(CRS.decode("4326"));
            } catch (FactoryException e) {
                LOGGER.error("Could not decode CRS 4326");
            }
        } else {
            newDataStore.forceSchemaCRS(collection.getSchema().getCoordinateReferenceSystem());
        }

        Transaction transaction = new DefaultTransaction("create");

        String typeName = newDataStore.getTypeNames()[0];
        FeatureStore<SimpleFeatureType, SimpleFeature> featureStore = (FeatureStore<SimpleFeatureType,
                SimpleFeature>) newDataStore.getFeatureSource(typeName);
        featureStore.setTransaction(transaction);
        try {
            featureStore.addFeatures(collection);
            transaction.commit();
        } catch (Exception problem) {
            transaction.rollback();
        } finally {
            transaction.close();
        }

        // Zip the shapefile
        String path = tempSHPfile.getAbsolutePath();
        String baseName = path.substring(0, path.length() - SHP.length());
        File shx = new File(baseName + ".shx");
        File dbf = new File(baseName + ".dbf");
        File prj = new File(baseName + ".prj");

        // mark created files for delete
        tempSHPfile.deleteOnExit();
        shx.deleteOnExit();
        dbf.deleteOnExit();
        prj.deleteOnExit();
        shpBaseDirectory.deleteOnExit();

        return shpBaseDirectory;
    }

}
